# -*- coding: utf-8 -*-
"""
@Time ： 2021/1/28 8:14
@Auth ： 张张呀
@File ：论文.py
@IDE ：PyCharm
@Motto：ABC(Always Be Coding)

"""
import os
import sys

import requests
from PyQt5 import QtCore, QtGui, QtWidgets
from bs4 import BeautifulSoup


class runthread(QtCore.QThread):
    #  通过类成员对象定义信号对象
    _signal = QtCore.pyqtSignal(str)

    def __init__(self, titleText, saveText):
        super(runthread, self).__init__()
        self.titleText = titleText
        self.saveText = saveText

    def __del__(self):
        self.wait()

    def run(self):
        if self.titleText == '' or self.saveText == '':  # 如果为空
            self._signal.emit('EMPTY')
            return
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
                   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                   'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
                   'Accept-Encoding': 'gzip, deflate, br',
                   'Connection': 'keep-alive',
                   'Upgrade-Insecure-Requests': '1'}
        if not self.titleText.endswith('.txt'):  # 如果不为目录（即单篇论文标题）
            self._signal.emit('SEARCH')
            downUrl = search_article(self.titleText)
            if downUrl == '':  # 如果搜索结果为空
                self._signal.emit('NULL')
                return
            else:
                try:
                    self._signal.emit('DOWNLOAD')
                    res = requests.get(downUrl, headers=headers, stream=True)
                    fileSize = int(res.headers['Content-Length'])
                    print(fileSize)
                    savedSize = 0
                    saveName = change_title(self.titleText)
                    with open('%s/%s.pdf' % (self.saveText, saveName), 'wb') as f:
                        for chunk in res.iter_content(chunk_size=1024):
                            if chunk:
                                f.write(chunk)
                                savedSize += len(chunk)
                                progress = int(savedSize / fileSize * 100)
                                self._signal.emit('PRO-%d' % progress)
                    self._signal.emit('SUCCESS')
                except:
                    self._signal.emit('FAILED')
        else:
            paperList = read_file(self.titleText)
            if paperList == '':
                self._signal.emit('FILEWRONG')
            else:
                error = []  # 用于记录失败的论文
                self._signal.emit('BATCH-%d' % len(paperList))
                for i in range(len(paperList)):
                    try:
                        downUrl = search_article(paperList[i])
                        print(downUrl)
                        pdf = download_article(downUrl)
                        saveName = change_title(paperList[i])
                        with open('%s/%s.pdf' % (self.saveText, saveName), 'wb') as f:
                            f.write(pdf)
                    except:
                        error.append(paperList[i])
                    self._signal.emit('NUM-%d' % (i + 1))
                with open('./errors.txt', 'w') as f:
                    for e in error:
                        f.write(e + '\n')
                self._signal.emit('COMPLETED-%d' % len(error))


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(600, 260)
        MainWindow.setMinimumSize(600, 260)
        MainWindow.setMaximumSize(600, 260)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("./1.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.titleLabel = QtWidgets.QLabel(self.centralwidget)
        self.titleLabel.setGeometry(QtCore.QRect(20, 56, 120, 31))
        self.titleLabel.setStyleSheet("font: 14pt \"Bauhaus 93\";")
        self.titleLabel.setObjectName("titleLabel")
        self.titleEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.titleEdit.setGeometry(QtCore.QRect(150, 55, 361, 30))
        self.titleEdit.setObjectName("titleEdit")
        self.titleButton = QtWidgets.QPushButton(self.centralwidget)
        self.titleButton.setGeometry(QtCore.QRect(520, 53, 61, 33))
        self.titleButton.setStyleSheet("font: 12pt \"Andalus\";")
        self.titleButton.setObjectName("titleButton")
        self.titleButton.clicked.connect(self.selectPaper)
        self.saveLabel = QtWidgets.QLabel(self.centralwidget)
        self.saveLabel.setGeometry(QtCore.QRect(20, 120, 120, 31))
        self.saveLabel.setStyleSheet("font: 14pt \"Bauhaus 93\";")
        self.saveLabel.setObjectName("saveLabel")
        self.saveEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.saveEdit.setGeometry(QtCore.QRect(150, 119, 361, 30))
        self.saveEdit.setObjectName("saveEdit")
        self.saveButton = QtWidgets.QPushButton(self.centralwidget)
        self.saveButton.setGeometry(QtCore.QRect(520, 117, 61, 33))
        self.saveButton.setStyleSheet("font: 12pt \"Andalus\";")
        self.saveButton.setObjectName("saveButton")
        self.saveButton.clicked.connect(self.selectSavepath)
        self.runButton = QtWidgets.QPushButton(self.centralwidget)
        self.runButton.setGeometry(QtCore.QRect(180, 196, 71, 31))
        self.runButton.setStyleSheet("font: 15pt \"Andalus\";\n"
                                     "background-color: rgb(122, 255, 65);\n"
                                     "border-radius: 10px;\n"
                                     "")
        self.runButton.setObjectName("runButton")
        self.runButton.clicked.connect(self.run)
        self.quitButton = QtWidgets.QPushButton(self.centralwidget)
        self.quitButton.setGeometry(QtCore.QRect(370, 196, 71, 31))
        self.quitButton.setStyleSheet("font: 15pt \"Andalus\";\n"
                                      "background-color: rgb(255, 87, 57);\n"
                                      "border-radius: 10px;\n"
                                      "")
        self.quitButton.setObjectName("quitButton")
        self.quitButton.clicked.connect(QtCore.QCoreApplication.instance().quit)
        self.searchLabel = QtWidgets.QLabel(self.centralwidget)
        self.searchLabel.setGeometry(QtCore.QRect(280, 196, 120, 20))
        self.searchLabel.setStyleSheet("font: 8pt \"Bauhaus 93\";")
        self.searchLabel.setObjectName("searchLabel")
        self.searchLabel.setVisible(False)
        self.progressBar = QtWidgets.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(180, 196, 300, 31))
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName("progressBar")
        self.progressBar.setVisible(False)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "一蓑烟雨任平生"))
        self.titleLabel.setText(_translate("MainWindow", "论文标题："))
        self.titleButton.setText(_translate("MainWindow", "选择"))
        self.saveLabel.setText(_translate("MainWindow", "存储目录："))
        self.saveButton.setText(_translate("MainWindow", "选择"))
        self.runButton.setText(_translate("MainWindow", "开始"))
        self.quitButton.setText(_translate("MainWindow", "退出"))
        self.searchLabel.setText(_translate("MainWindow", "搜索中..."))

    def selectPaper(self):
        fileName, filetype = QtWidgets.QFileDialog.getOpenFileName(self.centralwidget,
                                                                   "选取论文列表",
                                                                   os.getcwd(),
                                                                   "Text Files (*.txt)")
        self.titleEdit.setText(fileName)

    def selectSavepath(self):
        savePath = QtWidgets.QFileDialog.getExistingDirectory(self.centralwidget,
                                                              "选取保存目录",
                                                              os.getcwd())
        self.saveEdit.setText(savePath)

    def run(self):
        titleText = self.titleEdit.text()
        saveText = self.saveEdit.text()
        # 创建线程
        self.runthread = runthread(titleText, saveText)
        # 连接信号
        self.runthread._signal.connect(self.call_backrun)  # 进程连接回传到GUI的事件
        # 开始线程
        self.runthread.start()

    def call_backrun(self, msg):
        if msg == 'EMPTY':
            QtWidgets.QMessageBox.warning(self.centralwidget, '警告', '标题或目录为空！')
        if msg == 'SEARCH':
            self.runButton.setVisible(False)
            self.quitButton.setVisible(False)
            self.searchLabel.setVisible(True)
        if msg == 'NULL':
            self.runButton.setVisible(True)
            self.quitButton.setVisible(True)
            self.searchLabel.setVisible(False)
            QtWidgets.QMessageBox.information(self.centralwidget, '提示', '未搜到相应论文！')
        if msg == 'DOWNLOAD':
            self.searchLabel.setVisible(False)
            self.progressBar.setVisible(True)
            self.progressBar.setFormat('%%p')
            self.progressBar.setValue(0)
        if 'PRO' in msg:
            pro = int(msg.split('-')[-1])
            self.progressBar.setValue(pro)
        if msg == 'SUCCESS':
            self.progressBar.setVisible(False)
            self.runButton.setVisible(True)
            self.quitButton.setVisible(True)
            self.titleEdit.setText('')
            QtWidgets.QMessageBox.information(self.centralwidget, '提示', '论文下载完毕！')
        if msg == 'FAILED':
            self.progressBar.setVisible(False)
            self.runButton.setVisible(True)
            self.quitButton.setVisible(True)
            QtWidgets.QMessageBox.information(self.centralwidget, '提示', '论文下载失败！')
        if msg == 'FILEWRONG':
            QtWidgets.QMessageBox.information(self.centralwidget, '提示', '论文列表错误！')
        if 'BATCH' in msg:
            sumNumber = msg.split('-')[-1]
            self.runButton.setVisible(False)
            self.quitButton.setVisible(False)
            self.progressBar.setVisible(True)
            self.progressBar.setFormat('【%v/' + sumNumber + '】')
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(int(sumNumber))
            self.progressBar.setValue(0)
        if 'NUM' in msg:
            num = int(msg.split('-')[-1])
            self.progressBar.setValue(num)
        if 'COMPLETED' in msg:
            errorNum = int(msg.split('-')[-1])
            self.progressBar.setVisible(False)
            self.runButton.setVisible(True)
            self.quitButton.setVisible(True)
            self.titleEdit.setText('')
            QtWidgets.QMessageBox.information(self.centralwidget, '提示', '论文下载完毕！\n（%d个失败）' % errorNum)


def search_article(artName):
    '''
    搜索论文
    ---------------
    输入：论文名
    ---------------
    输出：搜索结果（如果没有返回""，否则返回PDF链接）
    '''
    url = 'https://www.sci-hub.ren/'
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
               'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
               'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
               'Accept-Encoding': 'gzip, deflate, br',
               'Content-Type': 'application/x-www-form-urlencoded',
               'Content-Length': '123',
               'Origin': 'https://www.sci-hub.ren',
               'Connection': 'keep-alive',
               'Upgrade-Insecure-Requests': '1'}
    data = {'sci-hub-plugin-check': '',
            'request': artName}
    res = requests.post(url, headers=headers, data=data)
    html = res.text
    soup = BeautifulSoup(html, 'html.parser')
    iframe = soup.find(id='pdf')
    if iframe == None:  # 未找到相应文章
        return ''
    else:
        downUrl = iframe['src']
        if 'http' not in downUrl:
            downUrl = 'https:' + downUrl
        return downUrl


def download_article(downUrl):
    '''
    根据论文链接下载文章
    ----------------------
    输入：论文链接
    ----------------------
    输出：PDF文件二进制
    '''
    headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0',
               'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
               'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
               'Accept-Encoding': 'gzip, deflate, br',
               'Connection': 'keep-alive',
               'Upgrade-Insecure-Requests': '1'}
    res = requests.get(downUrl, headers=headers)
    return res.content


def read_file(filePath):
    '''
    读取论文列表
    ----------------------
    输入：论文列表（.txt格式）
    ----------------------
    输出：论文标题的list
    '''
    try:
        with open(filePath, 'r') as f:
            paperList = f.readlines()
        paperList = [p.strip() for p in paperList]  # 去除换行符
        return paperList
    except:
        return ''


def change_title(title):
    '''
    修改论文名，以防由于特殊字符无法命名
    '''
    title = title.replace('\\', ' ')  # \
    title = title.replace('/', ' ')  # /
    title = title.replace(':', '：')  # :
    title = title.replace('*', '&')  # *
    title = title.replace('?', '？')  # ?
    title = title.replace('"', '“')  # "
    title = title.replace('<', '《')  # <
    title = title.replace('>', '》')  # >
    title = title.replace('|', ' ')  # |
    return title


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    widget = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()  # 类名
    ui.setupUi(widget)
    widget.show()
    sys.exit(app.exec_())
